/*
 * Jamus
 * Copyright (C) 2010 Jamus Team
 *
 * Jamus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jamus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jamus.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <exception>
#include <stdexcept>
#include <dlfcn.h>
#include <boost/foreach.hpp>

#include "PluginManager.h"
#include "config.h"

using namespace std;

PluginManager::PluginManager(const string& backendFilename, const vector<string>& frontendFilenames, const boost::property_tree::ptree& config)
{
	loadBackend(backendFilename, config);
	loadFrontends(frontendFilenames, config, *backend.backend);
}

PluginManager::~PluginManager()
{
	backend.destroyHandler(backend.backend);
	dlclose(backend.lib);

	BOOST_FOREACH(FrontendPlugin f, frontends) {
		f.destroyHandler(f.frontend);
		dlclose(f.lib);
	}
}

backend::Plugin& PluginManager::getBackend()
{
	assert(backend.backend);
	return *backend.backend;
}

PluginManager::FrontendList PluginManager::getFrontends()
{
	FrontendList list;
	BOOST_FOREACH(FrontendPlugin f, frontends)
		list.push_back(f.frontend);
	return list;
}

void PluginManager::loadBackend(const string& filename, const boost::property_tree::ptree& config)
{
	string absoluteFilename = PLUGIN_DIRECTORY "/" + filename;
	backend.lib = dlopen(absoluteFilename.c_str(), RTLD_NOW);
	if (!backend.lib)
		throw runtime_error(dlerror());

	dlerror(); // clear the error message

	// load the symbols
	backend::create_p* create_plugin = (backend::create_p*) dlsym(backend.lib, "create");
	const char* dlsym_error = dlerror();
	if(dlsym_error)
		throw runtime_error(dlsym_error);

	backend.destroyHandler = (backend::destroy_p*) dlsym(backend.lib, "destroy");
	dlsym_error = dlerror();
	if(dlsym_error)
		throw runtime_error(dlsym_error);

	// create an instance of the class
	backend.backend = create_plugin(config);
}

void PluginManager::loadFrontends(const vector<string>& filenames, const boost::property_tree::ptree& config, backend::Plugin& backend)
{
	assert(frontends.empty());
	frontends.reserve(filenames.size());

	BOOST_FOREACH(string filename, filenames) {
		FrontendPlugin f;

		string absoluteFilename = PLUGIN_DIRECTORY "/" + filename;
		f.lib = dlopen(absoluteFilename.c_str(), RTLD_NOW);
		if (!f.lib)
			throw runtime_error(dlerror());

		dlerror();

		// load the symbols
		frontend::create_p* create_plugin = (frontend::create_p*) dlsym(f.lib, "create");
		const char* dlsym_error = dlerror();
		if(dlsym_error)
			throw runtime_error(dlsym_error);

		f.destroyHandler = (frontend::destroy_p*) dlsym(f.lib, "destroy");
		dlsym_error = dlerror();
		if(dlsym_error)
			throw runtime_error(dlsym_error);

		// create an instance of the class
		f.frontend = create_plugin(config, backend);

		frontends.push_back(f);
	}
}
